#include "gamepanel.h"
#include "ui_gamepanel.h"
#include "playhand.h"
#include "endingpanel.h"
#include "datamanager.h"
#include <QPainter>
#include <QRandomGenerator>
#include <QDebug>
#include <QMouseEvent>
#include <QPropertyAnimation>

GamePanel::GamePanel(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::GamePanel)
{
    ui->setupUi(this);

    // 1. 添加背景图片
    int num = QRandomGenerator::global()->bounded(10);
    QString path = QString(":/images/background-%1.png").arg(num + 1);
    m_bkImage.load(path);

    // 2. 设置窗口的标题和大小
    setWindowTitle("欢乐斗地主");
    setFixedSize(1000, 650);

    // 3. 实例化游戏控制类对象
    gameControlInit();

    // 4. 更新玩家得分
    updatePlayerScore();

    // 5. 切割游戏图片
    initCardMap();

    // 6. 初始化游戏中的按钮组
    initButtonsGroup();

    // 7. 初始化玩家在窗口中的上下文环境
    initPlayerContext();

    // 8. 扑克牌场景的初始化
    initGameScene();

    // 9. 倒计时闹钟窗口的初始化
    initCountDown();

    // 定时器的实例化
    m_timer = new QTimer(this);
    connect(m_timer, &QTimer::timeout, this, &GamePanel::onDispatchCard);

    m_animation = new AnimationWindow(this);
    m_bgm = new BGMControl(this);

    // showEndingScorePanel();
    /*
    QTimer::singleShot(2000, this, [=](){
       showAnimationWindow(Plane);
    });
    */

    Communication* comm = DataManager::getInstance()->getCommunication();
    connect(comm, &Communication::startGame, this, [=](QByteArray msg) {
        initGamePanel(msg);
        gameStatusProcess(GameControl::DispatchCard);
        m_bgm->startBGM(80);
    });
}

GamePanel::~GamePanel()
{
    delete ui;
}

// 初始化游戏控制类信息
void GamePanel::gameControlInit()
{
    m_gameCtrl = new GameControl(this);
    m_gameCtrl->playerInit();
    // 得到三个玩家的实例对象
    Robot* leftRobot = m_gameCtrl->getLeftRobot();
    Robot* rightRobot = m_gameCtrl->getRightRobot();
    UserPlayer* user = m_gameCtrl->getUserPlayer();

    // 存储顺序 : 左侧机器人，右侧机器人，当前玩家
    m_playerList << leftRobot << rightRobot << user; // 添加到存储玩家实例化对象的容器中

    // 玩家状态改变信号     ------> 处理玩家状态改变的槽函数
    connect(m_gameCtrl, &GameControl::playerStatusChanged, this, &GamePanel::onPlayerStatusChanged);
    // 玩家抢地主下注信号   ------> 处理下注分数的槽函数
    connect(m_gameCtrl, &GameControl::notifyPlayerGrabLordBet, this, &GamePanel::onGrabLordBet);
    // 一轮游戏阶段改变信号 ------> 处理游戏阶段改变的槽函数
    connect(m_gameCtrl, &GameControl::gameStatusChanged, this, &GamePanel::gameStatusProcess);
    // 玩家出牌信号        ------> 显示玩家所出牌的槽函数
    connect(m_gameCtrl, &GameControl::notifyPlayHand, this, &GamePanel::onDisposePlayHand);

    // 发完牌的信号 ------> 处理玩家的手牌的槽函数
    connect(leftRobot, &Player::notifyPickCards, this, &GamePanel::disposeCard);
    connect(rightRobot, &Player::notifyPickCards, this, &GamePanel::disposeCard);
    connect(user, &Player::notifyPickCards, this, &GamePanel::disposeCard);

    // 将玩家的用户名添加到一个链表中，以便玩家分数的显示
    m_nameList << leftRobot->getName().toUtf8()
               << user->getName().toUtf8()
               << rightRobot->getName().toUtf8();
}

// 更新分数面板上面的分数
void GamePanel::updatePlayerScore()
{
    ui->scorePanel->setScores(m_playerList[0]->getScore(),
                              m_playerList[1]->getScore(),
                              m_playerList[2]->getScore()
                              );
}

// 切割并存储图片
void GamePanel::initCardMap()
{
    // 1. 加载整张大图片
    QPixmap pixmap(":/images/card.png");
    // 2. 计算每张扑克牌的大小
    m_cardSize.setWidth(pixmap.width() / 13);
    m_cardSize.setHeight(pixmap.height() / 5);

    // 3. 扑克牌背面的图片
    m_cardBackImage = pixmap.copy(m_cardSize.width() * 2, m_cardSize.height() * 4,
                                  m_cardSize.width(), m_cardSize.height()
                                 );

    // 裁剪出每一张扑克牌
    for (int i = 0, suit = Card::Suit_Begin + 1; suit < Card::Suit_End; ++suit, ++i) {
        for (int j = 0, pt = Card::Card_Begin + 1; pt < Card::Card_SJ; ++ pt, ++ j) {
            Card card(static_cast<Card::CardPoint>(pt), static_cast<Card::CardSuit>(suit));
            // 裁剪扑克牌
            cropImage(pixmap, m_cardSize.width() * j, m_cardSize.height() * i, card);
        }
    }

    // 设置大小王扑克牌
    Card c;
    c.setPoint(Card::Card_SJ);
    c.setSuit(Card::Suit_Begin);
    cropImage(pixmap, 0, m_cardSize.height() * 4, c);

    c.setPoint(Card::Card_BJ);
    c.setSuit(Card::Suit_Begin);
    cropImage(pixmap, m_cardSize.width(), m_cardSize.height() * 4, c);
}

// 裁剪图片
void GamePanel::cropImage(QPixmap &pix, int x, int y, const Card &c)
{
    QPixmap sub = pix.copy(x, y, m_cardSize.width(), m_cardSize.height());
    CardPanel* panel = new CardPanel(this);
    panel->setImage(sub, m_cardBackImage);
    panel->setCard(c);;
    panel->hide();
    m_cardMap.insert(c, panel);
    // 选牌的信号    ------> 处理玩家选牌的槽函数
    connect(panel, &CardPanel::cardSelected, this, &GamePanel::onCardSelected);
}

// 初始化按钮组
void GamePanel::initButtonsGroup()
{
    ui->btnGroup->initButtons();
    ui->btnGroup->selectPanel(ButtonGroup::Start);

    void (GamePanel::*startGame)() = &GamePanel::startGame;
    connect(ui->btnGroup, &ButtonGroup::startGame, this, startGame);
    connect(ui->btnGroup, &ButtonGroup::playHand, this, &GamePanel::onUserPlayHand);
    connect(ui->btnGroup, &ButtonGroup::pass, this, &GamePanel::onUserPass);
    connect(ui->btnGroup, &ButtonGroup::betPoint, this, [=](int bet){
        m_gameCtrl->getCurrentPlayer()->grabLordBet(bet);
        ui->btnGroup->selectPanel(ButtonGroup::Empty); // 下注完之后就隐藏对应的按钮
        // 判断当前游戏模式是否是网络模式
        if (DataManager::getInstance()->isNetworkMode()) {
            Message msg;
            msg.userName = DataManager::getInstance()->getUserName();
            msg.roomName = DataManager::getInstance()->getRoomName();
            msg.data1 = QByteArray::number(bet);
            msg.reqcode = RequestCode::GrabLord;
            DataManager::getInstance()->getCommunication()->sendMessage(&msg);
        }
    });
}

// 初始化玩家在窗口中的上下文环境
void GamePanel::initPlayerContext()
{
    // 1. 放置玩家扑克牌的区域
    const QRect cardsRect[] = {
        // x, y, width, height
        QRect(90, 130, 100, height() - 200),                        // 左侧机器人
        QRect(rect().right() - 190, 130, 100, height() - 200),     // 右侧机器人
        QRect(250, rect().bottom() - 120, width() - 500, 100)       // 当前玩家
    };

    // 2. 玩家出牌的区域
    const QRect playHandRect[] = {
        QRect(260, 150, 100, 100),                              // 左侧机器人
        QRect(rect().right() - 360, 150, 100, 100),             // 右侧机器人
        QRect(150, rect().bottom() - 290, width() - 300, 105)   // 当前玩家
    };

    // 3. 玩家头像显示的位置
    const QPoint roleImagePos[] = {
        QPoint(cardsRect[0].left() - 80, cardsRect[0].height() / 2 + 20),      // 左侧机器人
        QPoint(cardsRect[1].right() + 10, cardsRect[1].height() / 2 + 20),     // 右侧机器人
        QPoint(cardsRect[2].right() - 10, cardsRect[2].top() - 10)            // 当前玩家
    };

    // 循环初筛化玩家的上下文环境
    int index = m_playerList.indexOf(m_gameCtrl->getUserPlayer());
    for (int i = 0; i < m_playerList.size(); ++ i) {
        PlayerContext context;
        context.align = (i == index ? Horizontal : Vertical);
        context.isFrontSide = (i == index ? true : false);
        context.cardRect = cardsRect[i];
        context.playHandRect = playHandRect[i];
        // 提示信息
        context.info = new QLabel(this);
        context.info->resize(160, 98);
        context.info->hide();
        // 显示到出牌区域的中心位置
        QRect rect = playHandRect[i];
        QPoint pt(rect.left() + (rect.width() - context.info->width()) / 2,
                  rect.top() + (rect.height() - context.info->height()) / 2);
        context.info->move(pt);
        // 玩家的头像
        context.roleImage = new QLabel(this);
        context.roleImage->resize(84, 120);
        context.roleImage->hide();
        context.roleImage->move(roleImagePos[i]);
        m_contextMap.insert(m_playerList.at(i), context);
    }
}

// 初始化游戏场景
void GamePanel::initGameScene()
{
    // 1. 发牌区的扑克牌
    m_baseCard = new CardPanel(this);
    m_baseCard->setImage(m_cardBackImage, m_cardBackImage);
    // 2. 发牌过程中移动扑克牌
    m_moveCard = new CardPanel(this);
    m_moveCard->setImage(m_cardBackImage, m_cardBackImage);
    // 3. 最后的三张牌（用于窗口显示）
    for (int i = 0; i < 3; ++ i) {
        CardPanel* panel = new CardPanel(this);
        panel->setImage(m_cardBackImage, m_cardBackImage);
        m_last3Card.push_back(panel);
        panel->hide();
    }
    // 设置扑克牌的位置
    m_baseCardPos = QPoint((width() - m_cardSize.width()) / 2, height() / 2 - 100); // 中心靠上的位置
    m_baseCard->move(m_baseCardPos);
    m_moveCard->move(m_baseCardPos);

    int base = (width() - 3 * m_cardSize.width() - 2 * 10) / 2;
    for (int i = 0; i < 3; ++ i) {
        m_last3Card[i]->move(base + (m_cardSize.width() + 10) * i, 20);
    }

}

// 处理游戏的状态
void GamePanel::gameStatusProcess(GameControl::GameStatus status)
{
    // 记录游戏状态
    m_gameStatus = status;
    // 处理游戏状态
    switch (status) {
    case GameControl::DispatchCard:
        startDispatchCard();
        break;

    case GameControl::CallingLord:
    {
        // 取出预留的三张底牌数据
        CardList last3Card = m_gameCtrl->getSurplusCards().toCardList();
        // 给底牌窗口设置图片
        for (int  i = 0; i < last3Card.size(); ++ i) {
            QPixmap front = m_cardMap[last3Card.at(i)]->getImage();
            m_last3Card[i]->setImage(front, m_cardBackImage);
            m_last3Card[i]->hide();
        }
        // 开始叫地主
        m_gameCtrl->startLordCard();
        break;
    }

    case GameControl::PlayingHand:
        // 隐藏发牌区的底牌和移动的牌
        m_baseCard->hide();
        m_moveCard->hide();
        // 显示最后预留给地主的三张牌
        for (int i = 0;  i < m_last3Card.size(); ++ i) {
            m_last3Card.at(i)->show();
        }
        for (int i = 0; i < m_playerList.size(); ++ i) {
            PlayerContext& context = m_contextMap[m_playerList.at(i)];
            // 隐藏各个玩家抢地主过程中的提示信息
            context.info->hide();
            // 显示各个玩家的头像
            Player* player = m_playerList.at(i);
            QPixmap pixmap = loadRoleImage(player->getSex(), player->getDirection(), player->getRole());
            context.roleImage->setPixmap(pixmap);
            context.roleImage->show();
        }

        break;

    default:
        break;
    }
}

// 处理发牌状态
void GamePanel::startDispatchCard()
{
    // 重置每张卡牌的属性
    for (auto it = m_cardMap.begin();  it != m_cardMap.end(); ++ it) {
        it.value()->setSelected(false);
        it.value()->setFrontSide(true);
        it.value()->hide();
    }
    // 隐藏三张底牌
    for (int  i = 0; i < m_last3Card.size(); ++ i) {
        m_last3Card.at(i)->hide();
    }
    // 重置玩家的窗口上下文信息
    int index = m_playerList.indexOf(m_gameCtrl->getUserPlayer());
    for (int i = 0; i < m_playerList.size(); ++ i) {
        m_contextMap[m_playerList.at(i)].lastCards.clear();
        m_contextMap[m_playerList.at(i)].info->hide();
        m_contextMap[m_playerList.at(i)].roleImage->hide();
        m_contextMap[m_playerList.at(i)].isFrontSide = (i == index ? true : false);
    }
    // 重置所有玩家的卡牌数据
    m_gameCtrl->resetCardsData();
    // 显示底牌
    m_baseCard->show();
    // 隐藏按钮面板
    ui->btnGroup->selectPanel(ButtonGroup::Empty);
    // 启动定时器
    m_timer->start(10);
    // 播放背景音乐
    m_bgm->playAssistMusic(BGMControl::AssistMusic::Dispatch);
}

// 移动扑克牌
void GamePanel::cardMoveStep(Player* player, int curPos)
{
    // 得到每个玩家扑克牌展示区域
    QRect cardRect = m_contextMap[player].cardRect;
    // 每个玩家扑克牌移动的单元步长
    const int unit[] = {
        (m_baseCardPos.x() - cardRect.right()) / 100,   // 左侧机器人
        (cardRect.left() - m_baseCardPos.x()) / 100,     // 右侧机器人
        (cardRect.top() - m_baseCardPos.y()) / 100      // 我自己
    };
    // 每次窗口移动的时候每个玩家对应的牌的实时坐标
    const QPoint pos[] = {
        QPoint(m_baseCardPos.x() - curPos * unit[0], m_baseCardPos.y()), // 左侧机器人
        QPoint(m_baseCardPos.x() + curPos * unit[1], m_baseCardPos.y()), // 右侧机器人
        QPoint(m_baseCardPos.x(), m_baseCardPos.y() + curPos * unit[2])  // 我自己
    };

    // 移动扑克牌窗口
    int index = m_playerList.indexOf(player);
    m_moveCard->move(pos[index]);

    // 临界状态的处理
    if (curPos == 0) {
        m_moveCard->show();
    } else if (curPos == 100) {
        m_moveCard->hide();
    }
}

// 定时器处理发牌的动作
void GamePanel::onDispatchCard()
{
    // 记录扑克牌的位置
    static int curMovePos = 0; // 移动的步长
    // 当前玩家
    Player* curPlayer = m_gameCtrl->getCurrentPlayer();
    if (curMovePos >= 100) {    // 此时扑克牌已近移动到对应玩家显示手牌的区域
        // 给当前玩家发一张牌
        Card card = m_gameCtrl->takeOneCard();
        curPlayer->storeDispatchCard(card);
        // Cards cs(card);
        // disposeCard(curPlayer, cs);
        // 切换玩家
        m_gameCtrl->setCurrentPlayer(curPlayer->getNextPlayer());
        curMovePos = 0;
        // 显示发牌动画
        cardMoveStep(curPlayer, curMovePos);
        // 判断扑克牌是否发完了
        if (m_gameCtrl->getSurplusCards().cardCount() == 3) {
            // 终止定时器
            m_timer->stop();
            // 切换游戏状态
            gameStatusProcess(GameControl::CallingLord);
            // 暂停发牌音乐的播放
            m_bgm->stopAssistMusic();
            return ;
        }
    }
    // 移动扑克牌
    cardMoveStep(curPlayer, curMovePos);
    curMovePos += 15;
}

// 处理分发得到的牌
void GamePanel::disposeCard(Player *player, const Cards &cards)
{
    Cards& myCards = const_cast<Cards&>(cards);
    CardList list = myCards.toCardList();
    for (int i = 0; i < list.size(); ++ i) {
        CardPanel* panel = m_cardMap[list.at(i)];
        panel->setOwner(player);
    }

    // 更新扑克牌在窗口中的显示
    updatePlayerCards(player);
}

// 更新扑克牌在窗口中的显示
void GamePanel::updatePlayerCards(Player *player)
{
    Cards cards = player->getCards();
    CardList list = cards.toCardList();

    m_cardsRect = QRect();
    m_userCards.clear();

    // 取出展示扑克牌的区域
    int cardSpace = 20; // 牌与牌之间的间隙（牌的显示需要重叠显示）
    QRect cardsRect = m_contextMap[player].cardRect;
    for (int i = 0; i < list.size(); ++ i) {
        CardPanel* cardPanel = m_cardMap[list.at(i)];
        cardPanel->show();
        cardPanel->raise(); // 让当前窗口显示在父窗口的最上层
        cardPanel->setFrontSide(m_contextMap[player].isFrontSide);

        // 扑克牌是水平显示还是垂直显示
        if (m_contextMap[player].align == Horizontal) {
            int leftX = cardsRect.left() + (cardsRect.width() - (list.size() - 1) * cardSpace - cardPanel->width()) / 2;
            int topY = cardsRect.top() + (cardsRect.height() - m_cardSize.height()) / 2;
            if (cardPanel->isSelected()) {
                topY -= 10; // 将所选中的扑克配上移10px 水平显示的扑克牌
            }
            cardPanel->move(leftX + cardSpace * i, topY);
            m_cardsRect = QRect(leftX, topY, cardSpace * i + m_cardSize.width(), m_cardSize.height());
            int curWidth = 0;
            if (list.size() - 1 == i) {
                curWidth = m_cardSize.width();
            } else {
                curWidth = cardSpace;
            }
            QRect cardRect(leftX + cardSpace * i, topY, curWidth, m_cardSize.height());
            m_userCards.insert(cardPanel, cardRect);
        } else {
            int leftX = cardsRect.left() + (cardsRect.width() - cardPanel->width()) / 2;
            int topY = cardsRect.top() + (cardsRect.height() - (list.size() - 1) * cardSpace - cardPanel->height()) / 2;
            cardPanel->move(leftX, topY + i * cardSpace);
        }
    }

    // 显示玩家打出去的牌
    // 1. 得到当前玩家的出牌区域以及本轮打出去的牌
    QRect playCardRect = m_contextMap[player].playHandRect;
    Cards lastCards = m_contextMap[player].lastCards;
    if (!lastCards.isEmpty()) {
        int playSpacing = 24; // 设置牌与牌重叠之间的间隙
        CardList lastCardList = lastCards.toCardList();
        CardList::ConstIterator itplayed = lastCardList.constBegin();
        for (int i = 0; itplayed != lastCardList.constEnd(); ++ itplayed, ++ i) {
            CardPanel* panel = m_cardMap[*itplayed];
            panel->setFrontSide(true); // 打出的牌必须正面显示
            panel->raise();
            // 2. 将打出的牌移动到该玩家的出牌区域
            if (m_contextMap[player].align == Horizontal) { // 牌的显示是水平区域
                int leftBase = playCardRect.left() + (playCardRect.width() - (lastCardList.size() - 1) * playSpacing - panel->width()) / 2;
                int top = playCardRect.top() + (playCardRect.height() - panel->height()) / 2;
                panel->move(leftBase + i * playSpacing, top);
            } else { // 牌的显示是垂直区域
                // 需要避免垂直显示区域的牌与水平区域显示的牌重叠
                int left = playCardRect.left() + (playCardRect.width() - panel->width()) / 2;
                int top = playCardRect.top();
                panel->move(left, top + i * playSpacing);
            }
            panel->show();
        }
    }
}

// 加载玩家的头像
QPixmap GamePanel::loadRoleImage(Player::Sex sex, Player::Direction direct, Player::Role role)
{
    // 存储对应的图片
    QVector<QString> lordMan;
    QVector<QString> lordWoman;
    QVector<QString> farmerMan;
    QVector<QString> farmerWoman;

    lordMan << ":/images/lord_man_1.png" << ":/images/lord_man_2.png";
    lordWoman << ":/images/lord_woman_1.png" << ":/images/lord_woman_2.png";
    farmerMan << ":/images/farmer_man_1.png" << ":/images/farmer_man_2.png";
    farmerWoman << ":/images/farmer_woman_1.png" << ":/images/farmer_woman_2.png";

    // 加载图片 QImage
    QImage image;
    int random = QRandomGenerator::global()->bounded(2);
    if (sex == Player::Man && role == Player::Lord) {
        image.load(lordMan.at(random));
    } else if (sex == Player::Man && role == Player::Farmer) {
        image.load(farmerMan.at(random));
    } else if (sex == Player::Woman && role == Player::Lord) {
        image.load(lordWoman.at(random));
    } else if (sex == Player::Woman && role == Player::Farmer) {
        image.load(farmerWoman.at(random));
    }

    QPixmap pixmap;
    if (direct == Player::Left) {
        pixmap = QPixmap::fromImage(image); // QImage ------> QPixmap
    } else {
        pixmap = QPixmap::fromImage(image.mirrored(true, false)); // 将图片镜像翻转
    }

    return pixmap;
}

// 开始游戏
void GamePanel::startGame()
{
    int index = QRandomGenerator::global()->bounded(3) ;
    startGame(index + 1);
}

void GamePanel::startGame(int index)
{
    // 设置当前被发牌的玩家
    m_gameCtrl->setCurrentPlayer(index);
    // 界面的初始化
    ui->btnGroup->selectPanel(ButtonGroup::Empty);
    // m_gameCtrl->clearPlayerScore();
    updatePlayerScore();
    // 修改游戏状态  ------> 发牌阶段
    gameStatusProcess(GameControl::DispatchCard);
    // 开始播放背景音乐
    m_bgm->startBGM(80);
}

// 处理网络模式下玩家的发牌次序与分数
void GamePanel::initGamePanel(QByteArray msg)
{
    int index = 1; // 出牌次序
    // 约定的数据格式: 用户名-出牌次序-分数#用户名-出牌次序-分数#用户名-出牌次序-分数#
    orderMap order;
    auto lst = msg.left(msg.length() - 1).split('#');
    for (const auto& item : lst) {
        auto sub = item.split('-');
        order.insert(sub.at(1).toInt(), QPair(sub.at(0), sub.at(2).toInt()));
        if (sub.at(0) == DataManager::getInstance()->getUserName()) {
            // 通过用户名确定玩家的次序
            index = sub.at(1).toInt();
        }
    }
    updatePlayerInfo(order);
    // 开始游戏
    startGame(index);
}

// 更新用户名和分数
void GamePanel::updatePlayerInfo(orderMap &info)
{
    int lscore = 0, rscore = 0, mscore = 0;
    QByteArray left, right, mid;
    QByteArray current = DataManager::getInstance()->getUserName();
    // 取出玩家的名字和分数
    // 当前玩家的发牌次序为1
    if (current == info.value(1).first) {
        mid = info.value(1).first;
        right = info.value(2).first;
        left = info.value(3).first;
        mscore = info.value(1).second;
        rscore = info.value(2).second;
        lscore = info.value(3).second;
    }
    // 当前玩家的发牌次序为2
    else if (current == info.value(2).first) {
        mid = info.value(2).first;
        right = info.value(3).first;
        left = info.value(1).first;
        mscore = info.value(2).second;
        rscore = info.value(3).second;
        lscore = info.value(1).second;
    }
    // 当前玩家的发牌次序为3
    else if (current == info.value(3).first) {
        mid = info.value(3).first;
        right = info.value(1).first;
        left = info.value(2).first;
        mscore = info.value(3).second;
        rscore = info.value(1).second;
        lscore = info.value(2).second;
    }
    // 将数据显示到分数面板
    ui->scorePanel->setPlayerName(left, mid, right);
    // 将解析出的数据设置给各个玩家
    m_gameCtrl->getLeftRobot()->setScore(lscore);
    m_gameCtrl->getRightRobot()->setScore(rscore);
    m_gameCtrl->getUserPlayer()->setScore(mscore);
    m_gameCtrl->getLeftRobot()->setName(left);
    m_gameCtrl->getRightRobot()->setName(right);
    m_gameCtrl->getUserPlayer()->setName(mid);
    // 存储玩家的名字
    m_nameList.clear(); // 清空上一次的数据
    qDebug() << "left : " << left << "mid : " << mid  << "right : " << right;
    m_nameList << left << mid << right;
}

// 通知服务器当前玩家已经出牌了
void GamePanel::notifyOtherPlayHand(Cards &cs)
{
    DataManager* instance = DataManager::getInstance();
    if (instance->isNetworkMode()) {
        Message msg;
        msg.userName = instance->getUserName();
        msg.roomName = instance->getRoomName();
        msg.data1 = QByteArray::number(cs.cardCount());
        QDataStream stream(&msg.data2, QIODevice::ReadWrite);
        // 将cs转换为容器类型
        CardList cds = cs.toCardList();
        for (const auto& item : cds) {
            stream << item;
        }
        msg.reqcode = RequestCode::PlayAHand;
        instance->getCommunication()->sendMessage(&msg);
    }
}

// 处理玩家状态的变化，更新窗口状态显示
void GamePanel::onPlayerStatusChanged(Player *player, GameControl::PlayerStatus status)
{
    switch (status) {
    case GameControl::ThinkingForCallLord:
        if (player == m_gameCtrl->getUserPlayer()) {
            ui->btnGroup->selectPanel(ButtonGroup::CallLord, m_gameCtrl->getPlayerMaxBet());
        }
        break;

    case GameControl::ThinkingForPlayHand:
        // 隐藏上一轮打出的牌
        hidePlayerDropCards(player);
        if (player == m_gameCtrl->getUserPlayer()) { // 当前玩家是用户玩家
            // 取出已出牌玩家的对象
            Player* pendPlayer = m_gameCtrl->getPendPlayer();
            // 当前玩家就是出牌玩家
            if (pendPlayer == m_gameCtrl->getUserPlayer() || pendPlayer == nullptr) {
                ui->btnGroup->selectPanel(ButtonGroup::PlayCard); // 显示出牌按钮组
            } else {
                ui->btnGroup->selectPanel(ButtonGroup::PassOrPlay);
            }
        } else {
            ui->btnGroup->selectPanel(ButtonGroup::Empty);
        }

        break;

    case GameControl::Winning:
    {
        // 玩家获胜，终止背景音乐的播放
        m_bgm->stopBGM();
        m_contextMap[m_gameCtrl->getLeftRobot()].isFrontSide = true;
        m_contextMap[m_gameCtrl->getRightRobot()].isFrontSide = true;
        // 一轮游戏结束后，需要将未出完玩家的手牌全部正面展示出来
        updatePlayerCards(m_gameCtrl->getLeftRobot());
        updatePlayerCards(m_gameCtrl->getRightRobot());
        ui->btnGroup->selectPanel(ButtonGroup::Empty);
        // 更新玩家的得分
        updatePlayerScore();
        showEndingScorePanel();
        m_gameCtrl->setCurrentPlayer(player); // 本轮第一个出完牌的玩家在下一轮游戏获得优先叫地主的资格
        Message msg{
            .userName = DataManager::getInstance()->getUserName(),
            .roomName = DataManager::getInstance()->getRoomName(),
            .data1 = QByteArray::number(m_gameCtrl->getUserPlayer()->getScore()),
            .reqcode = RequestCode::GameOver
        };
        DataManager::getInstance()->getCommunication()->sendMessage(&msg);
        break;
    }

    default:
        break;
    }
}

// 处理玩家抢地主的信号，并显示玩家是否叫地主的特效
void GamePanel::onGrabLordBet(Player *player, int bet, bool flag)
{
    // 显示抢地主的信息提示
    PlayerContext context = m_contextMap[player];
    if (bet == 0) {
        context.info->setPixmap(QPixmap(":/images/buqiang.png"));
    } else {
        if (flag) // 第一个玩家
        {
            context.info->setPixmap(QPixmap(":/images/jiaodizhu.png"));
        } else {
            context.info->setPixmap(QPixmap(":/images/qiangdizhu.png"));
        }
        context.info->show();
    }

    // 显示叫地主的分数
    showAnimationWindow(AnimationType::Bet, bet);

    // 播放分数的背景音乐
    m_bgm->playerRobLordMusic(bet, static_cast<BGMControl::RoleSex>(player->getSex()), flag);
}

// 处理玩家的出牌
void GamePanel::onDisposePlayHand(Player *player, const Cards &cards)
{
    // 存储玩家打出的牌
    auto it = m_contextMap.find(player);
    it->lastCards = cards;

    // 根据牌型展示对应的游戏特效
    Cards& myCards = const_cast<Cards&>(cards);
    PlayHand hand(myCards);
    PlayHand::HandType type = hand.getHandType();
    if (type == PlayHand::Hand_Plane ||
        type == PlayHand::Hand_Plane_Two_Pair ||
        type == PlayHand::Hand_Plane_Two_Single) {
        showAnimationWindow(Plane);
    } else if (type == PlayHand::Hand_Seq_Pair) {
        showAnimationWindow(LianDui);
    } else if (type == PlayHand::Hand_Seq_Single) {
        showAnimationWindow(ShunZi);
    } else if (type  == PlayHand::Hand_Bomb) {
        showAnimationWindow(Bomb);
    } else if (type == PlayHand::Hand_Bomb_Jokers) {
        showAnimationWindow(JokerBomb);
    }
    // 如果玩家打出的是空牌(不出牌), 显示“不出”的提示信息
    if(cards.isEmpty())
    {
        it->info->setPixmap(QPixmap(":/images/pass.png"));
        it->info->show();
        m_bgm->playPassMusic(static_cast<BGMControl::RoleSex>(player->getSex()));
    } else {
        // 玩家第一个出牌与不是第一个出牌所播放的音乐不同
        if (m_gameCtrl->getPendPlayer() == player || m_gameCtrl->getPendPlayer() == nullptr) {
            m_bgm->playCardMusic(cards, true, static_cast<BGMControl::RoleSex>(player->getSex()));
        } else {
            m_bgm->playCardMusic(cards, false, static_cast<BGMControl::RoleSex>(player->getSex()));
        }
    }
    // 3. 更新玩家剩余的牌
    updatePlayerCards(player);

    // 4. 播放提示音乐
    // 判断玩家剩余牌的数量
    if (player->getCards().cardCount() == 2) {
        m_bgm->playLastMusic(BGMControl::CardType::Last2, static_cast<BGMControl::RoleSex>(player->getSex()));
    } else if (player->getCards().cardCount() == 1) {
        m_bgm->playLastMusic(BGMControl::CardType::Last1, static_cast<BGMControl::RoleSex>(player->getSex()));
    }
}

// 处理玩家选牌
void GamePanel::onCardSelected(Qt::MouseButton button)
{
    // 1. 判断是否处于出牌阶段
    if (m_gameStatus == GameControl::DispatchCard || m_gameStatus == GameControl::CallingLord) {
        return ;
    }

    // 2. 判断发出信号的所有者是不是当前玩家
    CardPanel* panel = static_cast<CardPanel*>(sender()); // 在当前信号处理函数里面得到对应的信号发出者的对象
    if (panel->getOwner() != m_gameCtrl->getUserPlayer()) {
        return ;
    }

    // 3. 保存当前被选中的牌的窗口对象
    m_curSelCard = panel;

    // 4. 判断参数的鼠标键是左键还是右键   左键选牌    右键出牌    双击左键两下取消所选中的牌
    if (button == Qt::LeftButton) {
        // 将扑克牌设置为选中状态
        panel->setSelected(!panel->isSelected());
        // 设置扑克牌在窗口中的显示
        updatePlayerCards(panel->getOwner());
        // 保存或则删除扑克牌窗口对象
        QSet<CardPanel*>::const_iterator it = m_selectCards.find(panel);
        if (it == m_selectCards.constEnd()) {
            m_selectCards.insert(panel);
        } else {
            m_selectCards.erase(it);
        }
        m_bgm->playAssistMusic(BGMControl::AssistMusic::SelectCard);
    } else if (button == Qt::RightButton) {
        // 调用出牌按钮的槽函数
        onUserPlayHand();
    }

}

// 处理玩家出牌
void GamePanel::onUserPlayHand()
{
    // 判断游戏是否处于出牌阶段
    if (m_gameStatus != GameControl::PlayingHand) {
        return ;
    }
    // 判断玩家是不是用户玩家
    if (m_gameCtrl->getCurrentPlayer() != m_gameCtrl->getUserPlayer()) {
        return ;
    }
    // 判读要出的牌是否时空牌
    if (m_selectCards.isEmpty()) {
        return ;
    }
    // 判断打出去的牌是否合理
    Cards cs;
    for (auto it = m_selectCards.begin(); it != m_selectCards.end(); ++ it) {
        Card card = (*it)->getCard();
        cs.add(card);
    }
    PlayHand hand(cs);
    PlayHand::HandType type = hand.getHandType();
    if (type == PlayHand::HandType::Hand_Unknown) {
        return ;
    }
    // 判断当前玩家的牌是否能压住上一个玩家所出的牌
    if (m_gameCtrl->getPendPlayer() != m_gameCtrl->getUserPlayer()) { // 当前玩家不是出牌玩家
        Cards cards = m_gameCtrl->getPendCards();
        if (!hand.canBeat(PlayHand(cards))) {
            return;
        }
    }
    m_countDown->stopCountDown(); // 玩家已经点击出牌按钮出牌，停止倒计时
    // 打出牌
    m_gameCtrl->getUserPlayer()->playHand(cs);
    notifyOtherPlayHand(cs);
    // 清空打出去的手牌
    m_selectCards.clear();
}

// 处理玩家放弃出牌
void GamePanel::onUserPass()
{
    m_countDown->stopCountDown(); // 玩家已经点击放弃出牌的按钮，停止倒计时
    // 判断是不是用户玩家
    Player* curPlayer = m_gameCtrl->getCurrentPlayer();
    Player* userPlayer = m_gameCtrl->getUserPlayer();
    if (curPlayer != userPlayer) {
        return;
    }
    // 判断当前用户玩家是不是上一次出牌的玩家（如果所出的牌过大，没有人要得起，则此玩家又会成为出牌玩家）
    Player* pendPlayer = m_gameCtrl->getPendPlayer();
    // 当首轮出拍时，已出牌玩家为空
    if (pendPlayer == userPlayer || pendPlayer == nullptr) {
        return ;
    }
    // 打出一张空牌
    Cards emptyCard;
    userPlayer->playHand(emptyCard);
    notifyOtherPlayHand(emptyCard);
    // 清空用户玩家选择的牌(玩家可能选择了一些牌，但是去点击的放弃出牌按钮，这些牌就没有被打出去，但是已经被选中了, 需要取消它们的选中状态)
    for (auto it = m_selectCards.begin(); it != m_selectCards.end(); ++ it) {
        (*it)->setSelected(false);
    }
    m_selectCards.clear(); // 将选中的手牌对象从容器中删除
    // 更新玩家待出牌的区域
    updatePlayerCards(userPlayer);
}

// 显示特效动画窗口
void GamePanel::showAnimationWindow(AnimationType type, int bet)
{
    switch(type) {
    case AnimationType::LianDui:
    case AnimationType::ShunZi:
        m_animation->setFixedSize(250, 150);
        m_animation->move((width()-m_animation->width()) / 2, 200);
        m_animation->showSequence(static_cast<AnimationWindow::Type>(type));
        break;
    case AnimationType::Plane:
        m_animation->setFixedSize(800, 75);
        m_animation->move((width()-m_animation->width()) / 2, 200);
        m_animation->showPlane();
        break;
    case AnimationType::Bomb:
        m_animation->setFixedSize(180, 200);
        m_animation->move((width()-m_animation->width())/2, (height() - m_animation->height()) / 2 - 70);
        m_animation->showBomb(static_cast<AnimationWindow::Type>(type));
        break;
    case AnimationType::JokerBomb:
        m_animation->setFixedSize(250, 200);
        m_animation->move((width()-m_animation->width())/2, (height() - m_animation->height()) / 2 - 70);
        m_animation->showBomb(static_cast<AnimationWindow::Type>(type));
        break;
    case AnimationType::Bet:
        m_animation->setFixedSize(160, 98);
        m_animation->move((width() -  m_animation->width()) / 2, (height() - m_animation->height()) / 2 - 140 );
        m_animation->showBetScore(bet);
        break;
    }
    m_animation->show();
}

// 隐藏玩家打出的牌
void GamePanel::hidePlayerDropCards(Player *player)
{
    auto it = m_contextMap.find(player);
    if (it != m_contextMap.end()) {
        // 如果为空，则上次玩家未出牌，则会出现“不出”的提示，需要将其隐藏
        if (it->lastCards.isEmpty()) {
            it->info->hide();
        } else {
            // Cards ------> Card
            CardList list = it->lastCards.toCardList();
            for (auto last = list.begin(); last != list.end(); ++ last) {
                m_cardMap[*last]->hide();
            }
        }
        it->lastCards.clear(); // 清空上轮所出的牌
    }
}

// 显示玩家的最终得分
void GamePanel::showEndingScorePanel()
{
    bool isLord = m_gameCtrl->getUserPlayer()->getRole() == Player::Lord ? true : false;
    bool isWin = m_gameCtrl->getUserPlayer()->isWin();
    EndingPanel* panel = new EndingPanel(isLord, isWin, this);
    panel->show();
    panel->move((width() - panel->width()) / 2,  -panel->width() / 2);
    panel->setPlayerScore(m_gameCtrl->getLeftRobot()->getScore(),
                          m_gameCtrl->getRightRobot()->getScore(),
                          m_gameCtrl->getUserPlayer()->getScore());
    panel->setPlayerName(m_nameList);
    if (isWin) {
        m_bgm->playEndingMusic(true);
    } else {
        m_bgm->playEndingMusic(false);
    }
    QPropertyAnimation* animation = new QPropertyAnimation(panel, "geometry", this);
    // 设置动画的持续时间
    animation->setDuration(1500); // 持续1.5s
    // 设置窗口的起始位置与终止位置
    animation->setStartValue(QRect(panel->x(), panel->y(), panel->width(), panel->height()));
    animation->setEndValue(QRect((width() - panel->width()) / 2, (height() - panel->height()) / 2,
                           panel->width(), panel->height()));
    // 设置窗口的运动曲线
    animation->setEasingCurve(QEasingCurve(QEasingCurve::OutBounce));
    // 播放动画效果
    animation->start();

    // 处理继续游戏的窗口信号
    connect(panel, &EndingPanel::continueGame, this, [=]() {
        panel->close();
        panel->deleteLater();
        animation->deleteLater();
        ui->btnGroup->selectPanel(ButtonGroup::Empty);

        if (DataManager::getInstance()->isNetworkMode()) {
            Message msg;
            if (DataManager::getInstance()->isManualMode()) {
                // 手动加入房间模式，重新开始下一轮游戏
                msg.roomName = DataManager::getInstance()->getRoomName();
                msg.reqcode = RequestCode::Continue;
            } else {
                // 自动加入房间模式，重新开始下一轮游戏
                msg.reqcode = RequestCode::AutoRoom;
            }

            msg.userName = DataManager::getInstance()->getUserName();
            DataManager::getInstance()->getCommunication()->sendMessage(&msg);
        } else {
            gameStatusProcess(GameControl::DispatchCard);
            m_bgm->startBGM(80);
        }
    });
}

// 初始化闹钟倒计时窗口
void GamePanel::initCountDown()
{
    m_countDown = new CountDown(this);
    // 将倒计时闹钟显示到玩家出牌区域的中心偏上一点的位置
    m_countDown->move((width() - m_countDown->width()) / 2, (height() - m_countDown->height()) / 2 + 30 );
    // m_countDown->show();
    // 处理
    connect(m_countDown, &CountDown::notMuchTime, this, [=](){
        // 播放提示音乐
        m_bgm->playAssistMusic(BGMControl::AssistMusic::Alert);
    });
    // 倒计时闹钟超时信号 ------> 不出牌的槽函数
    connect(m_countDown, &CountDown::timeout, this, &GamePanel::onUserPass);
    UserPlayer* userPlayer = m_gameCtrl->getUserPlayer();
    connect(userPlayer, &UserPlayer::startCountDown, this, [=]() {
        // 如果当前玩家不是上次出牌玩家，并且不是第一轮第一个出牌的玩家
        if (userPlayer != m_gameCtrl->getPendPlayer() && m_gameCtrl->getPendPlayer() != nullptr) {
            m_countDown->showCountDown();
        }
    });
}

void GamePanel::paintEvent(QPaintEvent *ev)
{
    Q_UNUSED(ev);
    QPainter p(this);
    p.drawPixmap(rect(), m_bkImage);
}

// 鼠标移动事件
void GamePanel::mouseMoveEvent(QMouseEvent *ev)
{
    if (ev->buttons() & Qt::LeftButton) {
        QPoint pt = ev->pos(); //获取当前鼠标的位置
        if (!m_cardsRect.contains(pt)) {
            m_curSelCard = nullptr; // 为被选中
        } else {
            QList<CardPanel*> list = m_userCards.keys();
            for (int i = 0; i < list.size(); ++ i) {
                CardPanel* panel = list.at(i);
                if (m_userCards[panel].contains(pt) && m_curSelCard != panel) {
                    // 选中这张牌
                    panel->clicked();
                    m_curSelCard = panel;
                }
            }
        }
    }
}

// 窗口关闭事件
void GamePanel::closeEvent(QCloseEvent *ev)
{
    // 信号处理函数: GameModel::GameModel
    emit panelClose();
    // 当游戏主窗口被关闭时，则代表玩家离开房间，需要给服务器发送离开的请求
    if (DataManager::getInstance()->isNetworkMode()) {
        Message msg;
        msg.reqcode = RequestCode::LeaveRoom;
        msg.userName = DataManager::getInstance()->getUserName();
        msg.roomName = DataManager::getInstance()->getRoomName();
        DataManager::getInstance()->getCommunication()->sendMessage(&msg);
    }
    ev->accept();
    deleteLater();
}
